<!DOCTYPE HTML>
<!--generated with sswg-->
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<head>
    <title>array_tools</title>
    <link rel="stylesheet" href="../sswg.css">
    <link rel="stylesheet" href="../style.css">
    <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<div style="max-width:1200px; margin:auto;">
<div style="text-align:left;">
<h1 id="array_tools">
array_tools
</h1><gray>&lt;/&gt;</gray><a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py"> ursina.array_tools</a><br>
<h2 id="Functions">
Functions
</h2><h3 id="flatten_list()">
flatten_list()
</h3><code_block id="code_block_0"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_0)">copy</button>flatten_list(target_list=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L7" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#flatten_list" onclick="navigator.clipboard.writeText('#flatten_list')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="flatten_completely()">
flatten_completely()
</h3><code_block id="code_block_1"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_1)">copy</button>flatten_completely(target_list=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L12" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#flatten_completely" onclick="navigator.clipboard.writeText('#flatten_completely')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="enumerate_2d()">
enumerate_2d()
</h3><code_block id="code_block_2"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_2)">copy</button>enumerate_<yellow>2</yellow>d(lst=None)
</code_block>
<purple>usage: for (x, y), value in enumerate_2d(my_2d_list)</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L20" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#enumerate_2d" onclick="navigator.clipboard.writeText('#enumerate_2d')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="enumerate_3d()">
enumerate_3d()
</h3><code_block id="code_block_3"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_3)">copy</button>enumerate_<yellow>3</yellow>d(target_<yellow>3</yellow>d_list=None)
</code_block>
<purple>usage: for (x, y, z), value in enumerate_3d(my_3d_list)</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L32" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#enumerate_3d" onclick="navigator.clipboard.writeText('#enumerate_3d')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="chunk_list()">
chunk_list()
</h3><code_block id="code_block_4"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_4)">copy</button>chunk_list(target_list=None, chunk_size=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L304" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#chunk_list" onclick="navigator.clipboard.writeText('#chunk_list')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="rotate_2d_list()">
rotate_2d_list()
</h3><code_block id="code_block_5"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_5)">copy</button>rotate_<yellow>2</yellow>d_list(target_<yellow>2</yellow>d_list=None)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L310" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#rotate_2d_list" onclick="navigator.clipboard.writeText('#rotate_2d_list')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="rotate_3d_list()">
rotate_3d_list()
</h3><code_block id="code_block_6"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_6)">copy</button>rotate_<yellow>3</yellow>d_list(target_<yellow>3</yellow>d_list=None, clockwise=True)
</code_block>
<purple>rotates around the y (up) axis where 1 is 90 degrees clockwise</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L314" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#rotate_3d_list" onclick="navigator.clipboard.writeText('#rotate_3d_list')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="string_to_2d_list()">
string_to_2d_list()
</h3><code_block id="code_block_7"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_7)">copy</button>string_to_<yellow>2</yellow>d_list(string=None, char_value_map=None)
</code_block>
<purple>char_value_map default: {'.': 0, '#': 1}</purple><br>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L332" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#string_to_2d_list" onclick="navigator.clipboard.writeText('#string_to_2d_list')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="list_2d_to_string()">
list_2d_to_string()
</h3><code_block id="code_block_8"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_8)">copy</button>list_<yellow>2</yellow>d_to_string(target_<yellow>2</yellow>d_list=None, characters=<green>'.<gray>#'</green>)</gray>
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L361" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#list_2d_to_string" onclick="navigator.clipboard.writeText('#list_2d_to_string')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h3 id="sample_bilinear()">
sample_bilinear()
</h3><code_block id="code_block_9"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_9)">copy</button>sample_bilinear(target_<yellow>2</yellow>d_list: Array<yellow>2</yellow>D=None, x=None, <olive>y</olive>=None, clamp_if_outside=True)
</code_block>
<div class="links">&nbsp;&nbsp;<a href="https://github.com/pokepetter/ursina/tree/v8_0_1/ursina/array_tools.py#L382" alt="Source Code" title="View Source Code" style="font-size:.75em;">&lt;/&gt;</a>&nbsp;&nbsp;<a href="#sample_bilinear" onclick="navigator.clipboard.writeText('#sample_bilinear')" title="Copy permalink">◃-</a></div><br>
<hr></hr><br>
<h2 id="Examples">
Examples
</h2><br>
<code_block id="code_block_10"><button class="copy_code_button" onclick="copy_to_clipboard(code_block_10)">copy</button>&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.ursinastuff <purple>import</purple> _test


<purple>def</purple> flatten_list(target_list):
&nbsp;&nbsp;&nbsp;&nbsp;<purple>import</purple> itertools
&nbsp;&nbsp;&nbsp;&nbsp;return list(itertools.chain(*target_list))


<purple>def</purple> flatten_completely(target_list):
&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> i in target_list:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> isinstance(i, tuple | list):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield <purple>from</purple> flatten_list(i)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield i


<purple>def</purple> enumerate_<yellow>2</yellow>d(lst):&nbsp;&nbsp;&nbsp;&nbsp;<gray># usage: <purple>for</purple> (x, y), value in enumerate_<yellow>2</yellow>d(my_<yellow>2</yellow>d_list)</gray>
&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> isinstance(lst, Array<yellow>2</yellow>D):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width = lst.width
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height = lst.height
&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width = len(lst)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height = len(lst[<yellow>0</yellow>])
&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> y in <blue>range</blue>(height):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> x in <blue>range</blue>(width):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield (x, y), lst[x][y]


<purple>def</purple> enumerate_<yellow>3</yellow>d(target_<yellow>3</yellow>d_list):&nbsp;&nbsp; <gray># usage: <purple>for</purple> (x, y, z), value in enumerate_<yellow>3</yellow>d(my_<yellow>3</yellow>d_list)</gray>
&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> x, vertical_slice in enumerate(target_<yellow>3</yellow>d_list):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> y, log in enumerate(vertical_slice):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> z, value in enumerate(log):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield (x, y, z), value



@generate_properties_for_class()
class Array<yellow>2</yellow>D(list):
&nbsp;&nbsp;&nbsp;&nbsp;__slots__ = (<green>'width'</green>, <green>'height'</green>, <green>'default_value'</green>)

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> __init__(self, width:int=None, height:int=None, default_value=<yellow>0</yellow>, data:list[list]=None):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.default_value = default_value

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> data is <purple>not</purple> None:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<gray># initialize with provided data</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.width = len(data)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.height = len(data[<yellow>0</yellow>])
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> self.width == <yellow>0</yellow> or self.height == <yellow>0</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError("width and height must be &gt;= <yellow>1</yellow>.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> any(len(row) != self.height <purple>for</purple> row in data):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError("all rows in the data must have the same length.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super().__init__(data)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:&nbsp;&nbsp; <gray># Initialize with default values</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> width is None or height is None:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError("width and height must be provided <purple>if</purple> no data is given.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.width = int(width)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.height = int(height)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super().__init__([[self.default_value <purple>for</purple> _ in <blue>range</blue>(self.height)] <purple>for</purple> _ in <blue>range</blue>(self.width)])

&nbsp;&nbsp;&nbsp;&nbsp;@staticmethod
&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> from_string(string, convert_to_type=str, starts_lower_left=True):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;string = string.strip()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> starts_lower_left:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data = [[convert_to_type(word.strip()) <purple>for</purple> word in line.split(<green>','</green>) <purple>if</purple> word] <purple>for</purple> line in string.split(<green>'\n'</green>) <purple>if</purple> line]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data = [list(row) <purple>for</purple> row in zip(*data[::-<yellow>1</yellow>], strict=True)]&nbsp;&nbsp;<gray># rotate</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return Array<yellow>2</yellow>D(data=data)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return Array<yellow>2</yellow>D(data=[[word.strip() <purple>for</purple> word in line.split(<green>','</green>) <purple>if</purple> word] <purple>for</purple> line in string.split(<green>'\n'</green>) <purple>if</purple> line])

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> to_string(self, separator=<green>', '</green>, always_separate=False):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines = []
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;longest_width = max(len(str(value)) <purple>for</purple> (_,_), value in enumerate_<yellow>2</yellow>d(self))

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> always_separate or longest_width &gt; <yellow>1</yellow>:&nbsp;&nbsp;&nbsp;&nbsp;<gray># separate each element with <green>', '</green></gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> y in <blue>range</blue>(self.height-<yellow>1</yellow>, -<yellow>1</yellow>, -<yellow>1</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line = separator.join([f<green>'{str(self[x][y]):&gt;{longest_width}}'</green> <purple>for</purple> x in <blue>range</blue>(self.width)])
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.append(line)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:&nbsp;&nbsp; <gray># make string without spaces since all the values are the same width</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> y in <blue>range</blue>(self.height-<yellow>1</yellow>, -<yellow>1</yellow>, -<yellow>1</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line = <green>''</green>.join([str(self[x][y]) <purple>for</purple> x in <blue>range</blue>(self.width)])
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.append(line)


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return <green>'\n'</green>.join(lines)

&nbsp;&nbsp;&nbsp;&nbsp;@property
&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> size(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.vec<yellow>2</yellow> <purple>import</purple> Vec<yellow>2</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return Vec2(self.width, self.height)

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> rows_getter(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return [[self[x][y] <purple>for</purple> x in <blue>range</blue>(self.width)] <purple>for</purple> y in <blue>range</blue>(self.height)]

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> columns_getter(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return [[self[x][y] <purple>for</purple> y in <blue>range</blue>(self.height)] <purple>for</purple> x in <blue>range</blue>(self.width)]

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> __str__(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines = self.to_string().strip().split(<green>'\n'</green>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;longest_number = int(len(str(self.height)))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> y in <blue>range</blue>(self.height-<yellow>1</yellow>, -<yellow>1</yellow>, -<yellow>1</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines[y] = f<green>'{self.height-<yellow>1</yellow>-y:&lt;{longest_number}}| {lines[y]}'</green>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;padding = longest_number + <yellow>2</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lines.append(f<green>'{"o":&lt;{padding}}{"-"*(self.width)}w:{self.width}'</green>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result = <green><green>'\n'</green></green>+<green><green>'\n'</green></green>.join(lines)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return result


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> map(self, func):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> (x, y), value in enumerate_<yellow>2</yellow>d(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self[x][y] = func(x, y, value)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> reset(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> x in <blue>range</blue>(self.width):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> y in <blue>range</blue>(self.height):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self[x][y] = self.default_value

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> paste(self, data, x, y, <olive>ignore</olive>=-<yellow>1</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> true_x in <blue>range</blue>(x, min(self.width, x+data.width)):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> true_y in <blue>range</blue>(y, min(self.height, y+data.height)):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> data[true_x-x][true_y-y] == ignore:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self[true_x][true_y] = data[true_x-x][true_y-y]


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> add_margin(self, top=<yellow>0</yellow>, right=<yellow>0</yellow>, bottom=<yellow>0</yellow>, left=<yellow>0</yellow>, value=None):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> top &lt; <yellow>0</yellow> or right &lt; <yellow>0</yellow> or right &lt; <yellow>0</yellow> or left &lt; <yellow>0</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError(<green>'input must be &gt;= <yellow>0</yellow>'</green>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> value is None:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;value = self.default_value

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_width = self.width + right + left
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_height = self.height + top + bottom
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_array = Array<yellow>2</yellow>D(new_width, new_height, default_value=value)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_array.paste(self, left, bottom)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return new_array

&nbsp;&nbsp;&nbsp;&nbsp;@property
&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> bounds(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.ursinamath <purple>import</purple> Bounds
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.vec<yellow>3</yellow> <purple>import</purple> Vec<yellow>3</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_x = self.width
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_y = self.height
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_x = <yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_y = <yellow>0</yellow>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> (x, y), value in enumerate_<yellow>2</yellow>d(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> value:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_x = min(min_x, x)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_y = min(min_y, y)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_x = max(max_x, x)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_y = max(max_y, y)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return Bounds(start=Vec3(min_x, min_y, <yellow>1</yellow>), end=Vec3(max_x, max_y, <yellow>1</yellow>))

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> get(self, x, y, default=<yellow>0</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x, y = int(x), int(y)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> x &lt; <yellow>0</yellow> or x &gt;= self.width or y &lt; <yellow>0</yellow> or y &gt;= self.height:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return default
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self[x][y]


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> set(self, x, y, value):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x, y = int(x), int(y)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> x &lt; <yellow>0</yellow> or x &gt;= self.width or y &lt; <yellow>0</yellow> or y &gt;= self.height:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self[x][y] = value


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> get_area(self, start, end, allow_out_of_bounds=False):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cropped_array = Array<yellow>2</yellow>D(width=end[<yellow>0</yellow>]-start[<yellow>0</yellow>], height=end[<yellow>1</yellow>]-start[<yellow>1</yellow>], default_value=self.default_value)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> <purple>not</purple> allow_out_of_bounds:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> (x, y), _ in enumerate_<yellow>2</yellow>d(cropped_array):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cropped_array[x][y] = self[int(x+start[<yellow>0</yellow>])][int(y+start[<yellow>1</yellow>])]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> (x, y), _ in enumerate_<yellow>2</yellow>d(cropped_array):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cropped_array[x][y] = self.get(x+start[<yellow>0</yellow>], y+start[<yellow>1</yellow>], default=self.default_value)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return cropped_array



<purple>if</purple> __name__ == <green>'__main__'</green>:
&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> textwrap <purple>import</purple> dedent, indent
&nbsp;&nbsp;&nbsp;&nbsp;grid = Array<yellow>2</yellow>D(width=<yellow>1</yellow><yellow>6</yellow>, height=<yellow>8</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;padded_grid = grid.add_margin(top=<yellow>4</yellow>, right=<yellow>7</yellow>, bottom=<yellow>3</yellow>, left=<yellow>2</yellow>, value=<yellow>7</yellow>)

&nbsp;&nbsp;&nbsp;&nbsp;_test(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Array<yellow>2</yellow>D(data=[[<yellow>1</yellow>,<yellow>6</yellow>], [<yellow>2</yellow>,<yellow>7</yellow>], [<yellow>3</yellow>,<yellow>8</yellow>], [<yellow>4</yellow>,<yellow>9</yellow>], [<yellow>5</yellow>,<yellow>1</yellow><yellow>0</yellow>]]).to_string()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;==
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;indent(dedent(<green>''</green>'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<yellow>6</yellow>,&nbsp;&nbsp;<yellow>7</yellow>,&nbsp;&nbsp;<yellow>8</yellow>,&nbsp;&nbsp;<yellow>9</yellow>, <yellow>1</yellow><yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<yellow>1</yellow>,&nbsp;&nbsp;<yellow>2</yellow>,&nbsp;&nbsp;<yellow>3</yellow>,&nbsp;&nbsp;<yellow>4</yellow>,&nbsp;&nbsp;<yellow>5</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<green>''</green><green>').strip(), '</green> ')
&nbsp;&nbsp;&nbsp;&nbsp;)

&nbsp;&nbsp;&nbsp;&nbsp;_test(Array<yellow>2</yellow>D(data=[[<yellow>1</yellow>,<yellow>6</yellow>], [<yellow>2</yellow>,<yellow>7</yellow>], [<yellow>3</yellow>,<yellow>8</yellow>], [<yellow>4</yellow>,<yellow>9</yellow>], [<yellow>5</yellow>,<yellow>1</yellow><yellow>0</yellow>]]).rows == [[<yellow>1</yellow>,<yellow>2</yellow>,<yellow>3</yellow>,<yellow>4</yellow>,<yellow>5</yellow>], [<yellow>6</yellow>,<yellow>7</yellow>,<yellow>8</yellow>,<yellow>9</yellow>,<yellow>1</yellow><yellow>0</yellow>]])

&nbsp;&nbsp;&nbsp;&nbsp;_test(Array<yellow>2</yellow>D.from_string(<green>''</green>'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<yellow>2</yellow>,<yellow>3</yellow>,<yellow>4</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<yellow>0</yellow>,<yellow>1</yellow>,<yellow>1</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<yellow>0</yellow>,<yellow>0</yellow>,<yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<green>''</green>', int) ==
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Array<yellow>2</yellow>D(data=[
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>0</yellow>,<yellow>0</yellow>,<yellow>2</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>0</yellow>,<yellow>1</yellow>,<yellow>3</yellow>,<yellow>1</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>0</yellow>,<yellow>1</yellow>,<yellow>4</yellow>,<yellow>0</yellow>]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;])
&nbsp;&nbsp;&nbsp;&nbsp;)


class Array<yellow>3</yellow>D(list):
&nbsp;&nbsp;&nbsp;&nbsp;__slots__ = (<green>'width'</green>, <green>'height'</green>, <green>'depth'</green>, <green>'default_value'</green>)

&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> __init__(self, width:int=None, height:int=None, depth:int=None, default_value=<yellow>0</yellow>, data:list[list]=None):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.default_value = default_value

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> data is <purple>not</purple> None:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<gray># initialize with provided data</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.width = len(data)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.height = len(data[<yellow>0</yellow>])
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.depth = len(data[<yellow>0</yellow>][<yellow>0</yellow>])
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> self.width == <yellow>0</yellow> or self.height == <yellow>0</yellow> or self.depth == <yellow>0</yellow>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError("width, height, depth must be &gt;= <yellow>1</yellow>.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> any(len(row) != self.height <purple>for</purple> row in data):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError("all rows in the data must have the same length.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super().__init__(data)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:&nbsp;&nbsp; <gray># Initialize with default values</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> width is None or height is None or depth is None:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raise ValueError("width and height and depth must be provided <purple>if</purple> no data is given.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.width = int(width)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.height = int(height)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self.depth = int(depth)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super().__init__([[[self.default_value <purple>for</purple> _ in <blue>range</blue>(self.depth)] <purple>for</purple> _ in <blue>range</blue>(self.height)] <purple>for</purple> _ in <blue>range</blue>(self.width)])


&nbsp;&nbsp;&nbsp;&nbsp;@property
&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> size(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.vec<yellow>3</yellow> <purple>import</purple> Vec<yellow>3</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return Vec3(self.width, self.height, self.depth)


&nbsp;&nbsp;&nbsp;&nbsp;@property
&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> bounds(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.ursinamath <purple>import</purple> Bounds
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.vec<yellow>3</yellow> <purple>import</purple> Vec<yellow>3</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_x = self.width
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_y = self.height
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_z = self.depth
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_x = <yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_y = <yellow>0</yellow>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_z = <yellow>0</yellow>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> (x, y, z), value in enumerate_<yellow>3</yellow>d(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> value:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_x = min(min_x, x)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_y = min(min_y, y)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;min_z = min(min_z, z)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_x = max(max_x, x)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_y = max(max_y, y)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;max_z = max(max_z, z)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return Bounds(start=Vec3(min_x, min_y, min_z), end=Vec3(max_x, max_y, max_z))


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> get(self, x, y, z, default=<yellow>0</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x, y, z = int(x), int(y), int(z)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return self[x][y][z]
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;except:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return default


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> reset(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> x,y,z, _ in enumerate_<yellow>3</yellow>d(self):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self[x][y][z] = self.default_value


&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> paste(self, data, x, y, z, <olive>ignore</olive>=-<yellow>1</yellow>):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> true_x in <blue>range</blue>(x, min(self.width, x+data.width)):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> true_y in <blue>range</blue>(y, min(self.height, y+data.height)):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> true_z in <blue>range</blue>(z, min(self.depth, y+data.depth)):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> data[true_x-x][true_y-y][true_z-z] == ignore:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;continue
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;self[true_x][true_y][true_z] = data[true_x-x][true_y-y][true_z-z]


<purple>def</purple> chunk_list(target_list, chunk_size):
&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> i in <blue>range</blue>(<yellow>0</yellow>, len(target_list), chunk_size):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;yield target_list[i:i + chunk_size]


<purple>def</purple> rotate_<yellow>2</yellow>d_list(target_<yellow>2</yellow>d_list):
&nbsp;&nbsp;&nbsp;&nbsp;return [list(row) <purple>for</purple> row in zip(*target_<yellow>2</yellow>d_list[::-<yellow>1</yellow>], strict=True)]&nbsp;&nbsp;<gray># rotate</gray>


<purple>def</purple> rotate_<yellow>3</yellow>d_list(target_<yellow>3</yellow>d_list, clockwise=True): <gray># rotates around the y (up) axis where <yellow>1</yellow> is <yellow>9</yellow><yellow>0</yellow> degrees clockwise</gray>
&nbsp;&nbsp;&nbsp;&nbsp;new_data = Array<yellow>3</yellow>D(width=target_<yellow>3</yellow>d_list.depth, height=target_<yellow>3</yellow>d_list.height, depth=target_<yellow>3</yellow>d_list.width)
&nbsp;&nbsp;&nbsp;&nbsp;<blue>print</blue>(<green>'rotate array<yellow>3</yellow>d, clockwise:'</green>, clockwise)

&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> clockwise:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> (x, y, z), value in enumerate_<yellow>3</yellow>d(target_<yellow>3</yellow>d_list):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_x = z
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_z = target_<yellow>3</yellow>d_list.width - <yellow>1</yellow> - x
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_data[new_x][y][new_z] = value
&nbsp;&nbsp;&nbsp;&nbsp;<purple>else</purple>:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<purple>for</purple> (x, y, z), value in enumerate_<yellow>3</yellow>d(target_<yellow>3</yellow>d_list):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_x = target_<yellow>3</yellow>d_list.depth - <yellow>1</yellow> - z
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_z = x
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_data[new_x][y][new_z] = value

&nbsp;&nbsp;&nbsp;&nbsp;return new_data


<purple>def</purple> string_to_<yellow>2</yellow>d_list(string, char_value_map=None): <gray># char_value_map default: {<green>'.'</green>: <yellow>0</yellow>, <green>'<gray>#'</green>: <yellow>1</yellow>}</gray>
&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> textwrap <purple>import</purple> dedent
&nbsp;&nbsp;&nbsp;&nbsp;<purple>if</purple> char_value_map is None:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char_value_map = {<green>'.'</green>: <yellow>0</yellow>, <green>'<gray>#'</green>: <yellow>1</yellow>}</gray>
&nbsp;&nbsp;&nbsp;&nbsp;grid = dedent(string).strip()
&nbsp;&nbsp;&nbsp;&nbsp;grid = grid.split(<green>'\n'</green>)
&nbsp;&nbsp;&nbsp;&nbsp;grid = [[char_value_map.get(e, <yellow>0</yellow>) <purple>for</purple> e in line] <purple>for</purple> line in grid]
&nbsp;&nbsp;&nbsp;&nbsp;grid = [list(row) <purple>for</purple> row in zip(*grid[::-<yellow>1</yellow>], strict=True)]&nbsp;&nbsp;<gray># rotate</gray>
&nbsp;&nbsp;&nbsp;&nbsp;return grid

<purple>if</purple> __name__ == <green>'__main__'</green>:
&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> ursina.ursinastuff <purple>import</purple> _test

&nbsp;&nbsp;&nbsp;&nbsp;_test(string_to_<yellow>2</yellow>d_list(<green>''</green>'\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.<gray>#<gray>#..<gray>#..<gray>#.<gray>#<gray>#<gray>#<gray>#..<gray>#..</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<green>''</green>')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;==
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rotate_<yellow>2</yellow>d_list([
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>0</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;])
&nbsp;&nbsp;&nbsp;&nbsp;)


<purple>def</purple> list_<yellow>2</yellow>d_to_string(target_<yellow>2</yellow>d_list, characters=<green>'.<gray>#'</green>):</gray>
&nbsp;&nbsp;&nbsp;&nbsp;return <green>'\n'</green>.join([<green>''</green>.join([characters[e] <purple>for</purple> e in line]) <purple>for</purple> line in target_<yellow>2</yellow>d_list])

<purple>if</purple> __name__ == <green>'__main__'</green>:
&nbsp;&nbsp;&nbsp;&nbsp;list_<yellow>2</yellow>d = [
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>0</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>,<yellow>1</yellow>, <yellow>0</yellow>, <yellow>0</yellow>,<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;]
&nbsp;&nbsp;&nbsp;&nbsp;<purple>from</purple> textwrap <purple>import</purple> dedent
&nbsp;&nbsp;&nbsp;&nbsp;expected_result = dedent(<green>''</green>'
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.<gray>#<gray>#..<gray>#..<gray>#.<gray>#<gray>#<gray>#<gray>#..<gray>#..</gray>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<green>''</green>').strip()

&nbsp;&nbsp;&nbsp;&nbsp;_test(list_<yellow>2</yellow>d_to_string(list_<yellow>2</yellow>d) == expected_result)


<purple>def</purple> sample_bilinear(target_<yellow>2</yellow>d_list:Array<yellow>2</yellow>D, x, y, clamp_if_outside=True):
&nbsp;&nbsp;&nbsp;&nbsp;X = clamp(int(x), <yellow>0</yellow>, target_<yellow>2</yellow>d_list.width-<yellow>1</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;Y = clamp(int(y), <yellow>0</yellow>, target_<yellow>2</yellow>d_list.height-<yellow>1</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;next_x = clamp(X+<yellow>1</yellow>, <yellow>0</yellow>, target_<yellow>2</yellow>d_list.width-<yellow>1</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;next_y = clamp(Y+<yellow>1</yellow>, <yellow>0</yellow>, target_<yellow>2</yellow>d_list.height-<yellow>1</yellow>)

&nbsp;&nbsp;&nbsp;&nbsp;height_bottom_left = target_<yellow>2</yellow>d_list[X][Y]
&nbsp;&nbsp;&nbsp;&nbsp;height_bottom_right = target_<yellow>2</yellow>d_list[next_x][Y]
&nbsp;&nbsp;&nbsp;&nbsp;height_top_left = target_<yellow>2</yellow>d_list[X][next_y]
&nbsp;&nbsp;&nbsp;&nbsp;height_top_right = target_<yellow>2</yellow>d_list[next_x][next_y]

&nbsp;&nbsp;&nbsp;&nbsp;x_fraction = x - X
&nbsp;&nbsp;&nbsp;&nbsp;y_fraction = y - Y

&nbsp;&nbsp;&nbsp;&nbsp;bottom_row_interpolation = (<yellow>1</yellow> - x_fraction) * height_bottom_left + x_fraction * height_bottom_right
&nbsp;&nbsp;&nbsp;&nbsp;top_row_interpolation = (<yellow>1</yellow> - x_fraction) * height_top_left + x_fraction * height_top_right
&nbsp;&nbsp;&nbsp;&nbsp;interpolated_height = (<yellow>1</yellow> - y_fraction) * bottom_row_interpolation + y_fraction * top_row_interpolation

&nbsp;&nbsp;&nbsp;&nbsp;return interpolated_height

<purple>if</purple> __name__ == <green>'__main__'</green>:
&nbsp;&nbsp;&nbsp;&nbsp;list_<yellow>2</yellow>d = Array<yellow>2</yellow>D(data=[
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>1</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[<yellow>1</yellow>,<yellow>0</yellow>,<yellow>0</yellow>],
&nbsp;&nbsp;&nbsp;&nbsp;])
&nbsp;&nbsp;&nbsp;&nbsp;_test(sample_bilinear(list_<yellow>2</yellow>d, <yellow>0</yellow>, <yellow>0</yellow>) == <yellow>1</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;_test(sample_bilinear(list_<yellow>2</yellow>d, <yellow>1</yellow>, <yellow>1</yellow>) == <yellow>0</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;_test(sample_bilinear(list_<yellow>2</yellow>d, .<yellow>5</yellow>, .<yellow>5</yellow>) == .<yellow>7</yellow><yellow>5</yellow>)
&nbsp;&nbsp;&nbsp;&nbsp;_test(sample_bilinear(list_<yellow>2</yellow>d, .<yellow>5</yellow>, .<yellow>7</yellow><yellow>5</yellow>) == <yellow>0</yellow>.<yellow>6</yellow><yellow>2</yellow><yellow>5</yellow>)



class LoopingList(list):
&nbsp;&nbsp;&nbsp;&nbsp;<purple>def</purple> __getitem__(self, i):
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return super().__getitem__(i % len(self))


</code_block>
<div class="sidebar"><span>8.0.1</span><br>
Basics<br>
&nbsp;&nbsp;<a href="ursina.html">Ursina</a><br>
&nbsp;&nbsp;<a href="entity.html">Entity</a><br>
&nbsp;&nbsp;<a href="button.html">Button</a><br>
&nbsp;&nbsp;<a href="sprite.html">Sprite</a><br>
&nbsp;&nbsp;<a href="text.html">Text</a><br>
&nbsp;&nbsp;<a href="audio.html">Audio</a><br>
<br>
Core Modules<br>
&nbsp;&nbsp;<a href="camera.html">camera</a><br>
&nbsp;&nbsp;<a href="mouse.html">mouse</a><br>
&nbsp;&nbsp;<a href="window.html">window</a><br>
&nbsp;&nbsp;<a href="application.html">application</a><br>
&nbsp;&nbsp;<a href="scene.html">scene</a><br>
<br>
Graphics<br>
&nbsp;&nbsp;<a href="color.html">color</a><br>
&nbsp;&nbsp;<a href="mesh.html">Mesh</a><br>
&nbsp;&nbsp;<a href="shader.html">Shader</a><br>
&nbsp;&nbsp;<a href="texture.html">Texture</a><br>
&nbsp;&nbsp;<a href="light.html">Light</a><br>
&nbsp;&nbsp;<a href="directional_light.html">DirectionalLight</a><br>
&nbsp;&nbsp;<a href="point_light.html">PointLight</a><br>
&nbsp;&nbsp;<a href="ambient_light.html">AmbientLight</a><br>
&nbsp;&nbsp;<a href="spot_light.html">SpotLight</a><br>
<br>
Procedural Models<br>
&nbsp;&nbsp;<a href="quad.html">Quad</a><br>
&nbsp;&nbsp;<a href="circle.html">Circle</a><br>
&nbsp;&nbsp;<a href="plane.html">Plane</a><br>
&nbsp;&nbsp;<a href="grid.html">Grid</a><br>
&nbsp;&nbsp;<a href="cone.html">Cone</a><br>
&nbsp;&nbsp;<a href="cylinder.html">Cylinder</a><br>
&nbsp;&nbsp;<a href="pipe.html">Pipe</a><br>
&nbsp;&nbsp;<a href="terrain.html">Terrain</a><br>
<br>
modules<br>
&nbsp;&nbsp;<a href="input_handler.html">input_handler</a><br>
&nbsp;&nbsp;<a href="mesh_importer.html">mesh_importer</a><br>
&nbsp;&nbsp;<a href="texture_importer.html">texture_importer</a><br>
&nbsp;&nbsp;<a href="string_utilities.html">string_utilities</a><br>
<br>
Animation<br>
&nbsp;&nbsp;<a href="animation.html">Animation</a><br>
&nbsp;&nbsp;<a href="frame_animation3d.html">FrameAnimation3d</a><br>
&nbsp;&nbsp;<a href="sprite_sheet_animation.html">SpriteSheetAnimation</a><br>
&nbsp;&nbsp;<a href="animator.html">Animator</a><br>
&nbsp;&nbsp;<a href="trail_renderer.html">TrailRenderer</a><br>
&nbsp;&nbsp;<a href="curve.html">curve</a><br>
<br>
Math<br>
&nbsp;&nbsp;<a href="ursinamath.html">ursinamath</a><br>
&nbsp;&nbsp;<a href="vec2.html">Vec2</a><br>
&nbsp;&nbsp;<a href="vec3.html">Vec3</a><br>
&nbsp;&nbsp;<a href="vec4.html">Vec4</a><br>
&nbsp;&nbsp;<a href="cubic_bezier.html">CubicBezier</a><br>
&nbsp;&nbsp;<a href="array_tools.html">array_tools</a><br>
&nbsp;&nbsp;<a href="array2_d.html">Array2D</a><br>
&nbsp;&nbsp;<a href="array3_d.html">Array3D</a><br>
<br>
Gameplay<br>
&nbsp;&nbsp;<a href="ursinastuff.html">ursinastuff</a><br>
&nbsp;&nbsp;<a href="sequence.html">Sequence</a><br>
&nbsp;&nbsp;<a href="func.html">Func</a><br>
&nbsp;&nbsp;<a href="keys.html">Keys</a><br>
<br>
Collision<br>
&nbsp;&nbsp;<a href="raycast.html">raycast</a><br>
&nbsp;&nbsp;<a href="terraincast.html">terraincast</a><br>
&nbsp;&nbsp;<a href="boxcast.html">boxcast</a><br>
&nbsp;&nbsp;<a href="hit_info.html">HitInfo</a><br>
&nbsp;&nbsp;<a href="collider.html">Collider</a><br>
&nbsp;&nbsp;<a href="box_collider.html">BoxCollider</a><br>
&nbsp;&nbsp;<a href="sphere_collider.html">SphereCollider</a><br>
&nbsp;&nbsp;<a href="mesh_collider.html">MeshCollider</a><br>
<br>
Prefabs<br>
&nbsp;&nbsp;<a href="sky.html">Sky</a><br>
&nbsp;&nbsp;<a href="editor_camera.html">EditorCamera</a><br>
&nbsp;&nbsp;<a href="tilemap.html">Tilemap</a><br>
&nbsp;&nbsp;<a href="first_person_controller.html">FirstPersonController</a><br>
&nbsp;&nbsp;<a href="platformer_controller2d.html">PlatformerController2d</a><br>
&nbsp;&nbsp;<a href="conversation.html">Conversation</a><br>
<br>
UI<br>
&nbsp;&nbsp;<a href="button.html">Button</a><br>
&nbsp;&nbsp;<a href="draggable.html">Draggable</a><br>
&nbsp;&nbsp;<a href="tooltip.html">Tooltip</a><br>
&nbsp;&nbsp;<a href="slider.html">Slider</a><br>
&nbsp;&nbsp;<a href="thin_slider.html">ThinSlider</a><br>
&nbsp;&nbsp;<a href="text_field.html">TextField</a><br>
&nbsp;&nbsp;<a href="input_field.html">InputField</a><br>
&nbsp;&nbsp;<a href="content_types.html">ContentTypes</a><br>
&nbsp;&nbsp;<a href="checkbox.html">Checkbox</a><br>
&nbsp;&nbsp;<a href="button_list.html">ButtonList</a><br>
&nbsp;&nbsp;<a href="button_group.html">ButtonGroup</a><br>
&nbsp;&nbsp;<a href="window_panel.html">WindowPanel</a><br>
&nbsp;&nbsp;<a href="file_browser.html">FileBrowser</a><br>
&nbsp;&nbsp;<a href="file_browser_save.html">FileBrowserSave</a><br>
&nbsp;&nbsp;<a href="dropdown_menu.html">DropdownMenu</a><br>
&nbsp;&nbsp;<a href="radial_menu.html">RadialMenu</a><br>
&nbsp;&nbsp;<a href="health_bar.html">HealthBar</a><br>
<br>
Editor<br>
&nbsp;&nbsp;<a href="hot_reloader.html">HotReloader</a><br>
&nbsp;&nbsp;<a href="grid_editor.html">GridEditor</a><br>
&nbsp;&nbsp;<a href="pixel_editor.html">PixelEditor</a><br>
&nbsp;&nbsp;<a href="ascii_editor.html">ASCIIEditor</a><br>
&nbsp;&nbsp;<a href="color_picker.html">ColorPicker</a><br>
<br>
Scripts<br>
&nbsp;&nbsp;<a href="grid_layout.html">grid_layout</a><br>
&nbsp;&nbsp;<a href="duplicate.html">duplicate</a><br>
&nbsp;&nbsp;<a href="smooth_follow.html">SmoothFollow</a><br>
&nbsp;&nbsp;<a href="scrollable.html">Scrollable</a><br>
&nbsp;&nbsp;<a href="noclip_mode.html">NoclipMode</a><br>
&nbsp;&nbsp;<a href="noclip_mode2d.html">NoclipMode2d</a><br>
&nbsp;&nbsp;<a href="build.html">build</a><br>
<br>
Assets<br>
&nbsp;&nbsp;<a href="models.html">models</a><br>
&nbsp;&nbsp;<a href="textures.html">textures</a><br>
<br>
Shaders<br>
&nbsp;&nbsp;<a href="unlit_shader.html">unlit_shader</a><br>
&nbsp;&nbsp;<a href="lit_with_shadows_shader.html">lit_with_shadows_shader</a><br>
&nbsp;&nbsp;<a href="matcap_shader.html">matcap_shader</a><br>
&nbsp;&nbsp;<a href="colored_lights_shader.html">colored_lights_shader</a><br>
&nbsp;&nbsp;<a href="fresnel_shader.html">fresnel_shader</a><br>
&nbsp;&nbsp;<a href="projector_shader.html">projector_shader</a><br>
&nbsp;&nbsp;<a href="texture_blend_shader.html">texture_blend_shader</a><br>
&nbsp;&nbsp;<a href="instancing_shader.html">instancing_shader</a><br>
&nbsp;&nbsp;<a href="triplanar_shader.html">triplanar_shader</a><br>
&nbsp;&nbsp;<a href="normals_shader.html">normals_shader</a><br>
&nbsp;&nbsp;<a href="transition_shader.html">transition_shader</a><br>
&nbsp;&nbsp;<a href="fxaa.html">fxaa</a><br>
&nbsp;&nbsp;<a href="ssao.html">ssao</a><br>
&nbsp;&nbsp;<a href="camera_outline_shader.html">camera_outline_shader</a><br>
&nbsp;&nbsp;<a href="pixelation_shader.html">pixelation_shader</a><br>
&nbsp;&nbsp;<a href="camera_contrast.html">camera_contrast</a><br>
&nbsp;&nbsp;<a href="camera_vertical_blur.html">camera_vertical_blur</a><br>
&nbsp;&nbsp;<a href="camera_grayscale.html">camera_grayscale</a><br>
</div><br>
<script>
function copy_to_clipboard(containerid) {
    var range = document.createRange()
    range.selectNode(containerid)
    window.getSelection().removeAllRanges()
    window.getSelection().addRange(range)
    document.execCommand("copy")
    window.getSelection().removeAllRanges()
}
</script>
<br>
<br>
</body>
</html><style>
h1 {margin-bottom:0em; margin-top:1em}
h2 {margin-bottom:.5em;}
h3 {margin:0em; font-size:1em;}
code_block {margin-top:.0em; margin-bottom:1em;}
hr {border-color:gray; opacity:.25;}
.links {width:fit-content;}
.links a {text-decoration:none; opacity:.5;}
</style>
